﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Metex
{
    public class Lexex
    {
        public string Input { get; private set; }

        private List<PatternMatcher> matchers = new List<PatternMatcher>();

        public Lexex(string input)
        {
            this.Input = input.Trim();
            BuildPatternMatchers();
        }

        public IToken GetNextToken()
        {
            IToken tok = GetNextNonWhitespaceToken();
            if (tok != null)
                return tok;
            else
                throw new ArgumentException("Não há mais tokens");
        }

        public bool HasNextToken()
        {
            return PeekNextNonWhitespaceToken() != null;
        }

        public IToken[] Analyze()
        {
            List<IToken> tokens = new List<IToken>();
            while (HasNextToken())
            {
                IToken token = GetNextNonWhitespaceToken();
                tokens.Add(token);
            }
            return tokens.ToArray();
        }

        private IToken GetNextNonWhitespaceToken()
        {
            IToken tok = PeekNextNonWhitespaceToken();
            if (tok != null)
            {
                ConsumeToken(tok);
                return tok;
            }
            else
            {
                return null;
            }
        }

        private IToken PeekNextNonWhitespaceToken()
        {
            while (Input.Length > 0)
            {
                IToken token = GetBestToken();
                if (token is WhitespaceToken)
                {
                    ConsumeToken(token);
                    continue;
                }
                return token;
            }
            return null;
        }


        private void ConsumeToken(IToken token)
        {
            Input = Input.Substring(token.Lexeme.Length);
        }

        private IToken GetBestToken()
        {
            foreach (PatternMatcher matcher in matchers)
            {
                if (matcher.TryMatch(Input))
                {
                    return matcher.Token;
                }
            }
            throw new ArgumentException("Token nao reconhecido: " + Input);
        }

        private void BuildPatternMatchers()
        {
            matchers.Add(new PatternMatcher(@"^\s+", new WhitespaceToken()));
            // = ou . ou { ou } ou [ ou ] ou ( ou ) ou | 
            matchers.Add(new PatternMatcher(@"^(=|\.|\{|\}|\[|\]|\(|\)|\|)", new WirthSymbolToken()));
            matchers.Add(new PatternMatcher("^\"[^\"]+\"", new TerminalToken()));
            matchers.Add(new PatternMatcher(@"^\w+", new NonTerminalToken()));
        }
    }
}
